// Enesea Win Device
// (c) jimon game studio

#include "CDevice_Win.h"
#include "jeThreads.h"
#include "CMouseDriver.h"
#include "CKeyDriver.h"
#include "ILogManager.h"

#ifdef JE_DEVICE_WIN

namespace je
{
	namespace device
	{
		CDevice_Win::DeviceListType CDevice_Win::DeviceList;

		//! Constructor
		CDevice_Win::CDevice_Win()
		{
			DeviceList.push_back(this);
		}

		//! Destructor
		CDevice_Win::~CDevice_Win()
		{
			for(DeviceListType::Iterator Iter = DeviceList.begin();Iter!=DeviceList.end();Iter++)
				if((*Iter) == this)
				{
					DeviceList.erase(Iter);
					break;
				}
		}

		//! Create Window
		IWindow * CDevice_Win::CreateWindow(const jeVector2di & Size,const jeStringd & Title,u1 FullScreen,s32 Depth,s32 Hertz,s32 MSAA,const void * PlatformIconId)
		{
			s32 IconId = -1;
			if(PlatformIconId)
				IconId = *reinterpret_cast<const s32*>(PlatformIconId);

			CWindow_Win * Window = NULL;
			JENEW(Window,CWindow_Win(Size,Title,FullScreen,Depth,Hertz,MSAA,IconId,WndProc))

			WindowList.push_back(Window);

			return Window;
		}

		//! Delete Window
		void CDevice_Win::DeleteWindow(IWindow * Window)
		{
			for(WindowListType::Iterator Iter = WindowList.begin();Iter!=WindowList.end();Iter++)
				if((*Iter)==Window)
				{
					WindowList.erase(Iter);
					Window->Drop();
					break;
				}
		}

		//! Device Poll
		void CDevice_Win::DevicePoll()
		{
			MSG msg;
			while(PeekMessage( &msg,0,0,0,PM_REMOVE))
			{
				TranslateMessage( &msg );
				DispatchMessage( &msg );
			}
		}

		//! Get Window By HWND
		CWindow_Win * CDevice_Win::GetWindowByWin32Handle(HWND hWnd)
		{
			CWindow_Win * Window = NULL;
			for(WindowListType::Iterator Iter = WindowList.begin();Iter!=WindowList.end();Iter++)
				if((*Iter)->GetWin32WindowHandle() == hWnd)
				{
					Window = *Iter;
					break;
				}
			return Window;
		}

		//! Win32 Callback function
		LRESULT CALLBACK CDevice_Win::WndProc(HWND hWnd,UINT uMsg,WPARAM wParam,LPARAM lParam)
		{
			CDevice_Win * CurrentDevice = NULL;
			CWindow_Win * CurrentWindow = NULL;

			for(DeviceListType::Iterator Iter = DeviceList.begin();Iter!=DeviceList.end();Iter++)
			{
				CDevice_Win * Dev = *Iter;
				CWindow_Win * Window = Dev->GetWindowByWin32Handle(hWnd);

				if(Window)
				{
					CurrentDevice = Dev;
					CurrentWindow = Window;
					break;
				}
			}

			if(!CurrentDevice || !CurrentWindow)
				return DefWindowProc(hWnd,uMsg,wParam,lParam);

			#ifndef WM_MOUSEWHEEL
				#define WM_MOUSEWHEEL 0x020A
			#endif
			#ifndef WHEEL_DELTA
				#define WHEEL_DELTA 120
			#endif

			#ifndef WM_XBUTTONDOWN
				#define WM_XBUTTONDOWN 0x020B
			#endif
			#ifndef WM_XBUTTONUP
				#define WM_XBUTTONUP 0x020C
			#endif
			#ifndef WM_XBUTTONDBLCLK
				#define WM_XBUTTONDBLCLK 0x020D
			#endif

			#define MOUSE_XBUTTON1 0x0001
			#define MOUSE_XBUTTON2 0x0002

			switch (uMsg)
			{
			case WM_PAINT:
				{
					PAINTSTRUCT ps;
					BeginPaint(hWnd, &ps);
					EndPaint(hWnd, &ps);
				}
				return 0;

			case WM_ERASEBKGND:
				{
					return 0;
				}

			case WM_ACTIVATE:
				{
					if (wParam)
						CurrentWindow->SetActive(true);
					else
						CurrentWindow->SetActive(false);
					return 0;
				}
			case WM_SYSCOMMAND:
				{
					switch (wParam)
					{
						case SC_SCREENSAVE:
						case SC_MONITORPOWER:
						return 0;
					}
					break;
				}
			case WM_CLOSE:
				{
					CurrentWindow->SetCloseFlag(true);
					PostQuitMessage(0);
					return 0;
				}
			case WM_SETFOCUS:
				{
					if(CurrentWindow->IsFullScreen())
					{
						CurrentWindow->SetFullScreen(true,CurrentWindow->GetSize(),CurrentWindow->GetDepth(),CurrentWindow->GetHertz());
					}
					CurrentWindow->SetActive(true);
					return 0;
				}
			case WM_DESTROY:
			case WM_KILLFOCUS:
				{
					if(CurrentWindow->IsFullScreen())
					{
						ChangeDisplaySettings(0,CDS_FULLSCREEN);
						ShowWindow(hWnd,SW_MINIMIZE);
					}
					CurrentWindow->SetActive(false);
					return 0;
				}
			case WM_KEYDOWN:
			case WM_KEYUP:
				{
					(static_cast<input::CKeyDriver*>(CurrentWindow->GetKeyDriver()))->SetDown((input::KeyCode)wParam,(WM_KEYDOWN == uMsg));
					return 0;
				}
			case WM_LBUTTONDOWN:
			case WM_LBUTTONUP:
				{
					(static_cast<input::CKeyDriver*>(CurrentWindow->GetKeyDriver()))->SetDown(input::KC_MOUSE_LEFT,(WM_LBUTTONDOWN == uMsg));
					return 0;
				}
			case WM_RBUTTONDOWN:
			case WM_RBUTTONUP:
				{
					(static_cast<input::CKeyDriver*>(CurrentWindow->GetKeyDriver()))->SetDown(input::KC_MOUSE_RIGHT,(WM_RBUTTONDOWN == uMsg));
					return 0;
				}
			case WM_MBUTTONDOWN:
			case WM_MBUTTONUP:
				{
					(static_cast<input::CKeyDriver*>(CurrentWindow->GetKeyDriver()))->SetDown(input::KC_MOUSE_MIDDLE,(WM_MBUTTONDOWN == uMsg));
					return 0;
				}
			case WM_XBUTTONDOWN:
			case WM_XBUTTONUP:
				{
					u32 ButtonNum = HIWORD(wParam);
					input::KeyCode Key;
					switch(ButtonNum)
					{
					case MOUSE_XBUTTON1:
						{
							Key = input::KC_XBUTTON1;
							break;
						}
					case MOUSE_XBUTTON2:
						{
							Key = input::KC_XBUTTON2;
							break;
						}
					default:
						{
							JELOG_E("Unknown button")
							Key = input::KC_XBUTTON1;
							break;
						}
					}
					(static_cast<input::CKeyDriver*>(CurrentWindow->GetKeyDriver()))->SetDown(Key,(WM_XBUTTONDOWN == uMsg));
					return true; // check msdn for this
				}
			case WM_MOUSEWHEEL:
				{
					(static_cast<input::CMouseDriver*>(CurrentWindow->GetMouseDriver()))->SetMouseWheelPosition( CurrentWindow->GetMouseDriver()->GetMouseWheelPosition() + ((short)HIWORD(wParam)) / (s32)WHEEL_DELTA );
					return 0;
				}
			case WM_SIZE:
				{
					CurrentWindow->Resize(jeVector2di(LOWORD(lParam),HIWORD(lParam)));
					return 0;
				}
			};

			return DefWindowProc(hWnd,uMsg,wParam,lParam);
		}

	}
}

#endif
